import datetime
import sys
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3 import Retry

# 将工程目录加入包扫描
currPath = os.path.abspath(os.path.dirname(__file__))
srcPath = os.path.split(currPath)[0]
rootPath = os.path.split(srcPath)[0]
projectPath = os.path.split(rootPath)[0]
sys.path.append(projectPath)

from src.db.LocalDb import LocalDb
from src.utils import CommonUtils
from src.utils.LogUtils import Logger
from src.config.CcxInsightConfig import Profile
from src.db.CcxDataCenterDb import CcxDataCenterProd


class ApiCaller:
    def __init__(self, logger: Logger, connect_timeout: int = 10, read_timeout: int = 10, retry: int = 3, backoff_factor: float = 0.5, default_headers: dict = None):
        self.logger = logger
        self.connect_timeout = connect_timeout
        self.read_timeout = read_timeout
        self.default_headers = default_headers
        retry = Retry(total=retry, backoff_factor=backoff_factor)
        adapter = HTTPAdapter(max_retries=retry)
        session = requests.session()
        session.mount("http://", adapter)
        session.mount("https://", adapter)
        self.session = session

    def post(self, url, data, headers=None):
        try:
            self.logger.info(f"API调用: {url} {data}")
            response = self.session.post(url=url,
                                         data=data,
                                         headers=headers if headers is not None else self.default_headers)
            if response is None:
                self.logger.error(f"API调用 失败 url:{url}, API返回结果为None")
                return None
            if response.status_code == 200:
                self.logger.info(f"API调用 成功: {url} HTTP_STATUS：{response.status_code} {response.text[:200]}")
            else:
                self.logger.error(f"API调用 失败 {url}, HTTP_STATUS：{response.status_code}")
                return None
            apiResult = response.json()
            if apiResult["code"] != 1:
                self.logger.error(f"API调用 失败 url:{url}, message:{apiResult['msg']}")
                return None
            return apiResult["result"]
        except Exception as e:
            self.logger.error(f"API调用异常 url:{url}", e)
        return None


class SyncBondNewValuation:
    def __init__(self, logger: Logger, localDb: LocalDb, dcDb: CcxDataCenterProd):
        self.logger = logger
        self.localDb = localDb
        self.dcDb = dcDb
        self.size = 100
        self.cacheSecode = {}
        self.cacheEnterprise = {}
        self.isAll = False
        self.columns = ["id", "publishDate", "issuingDate", "valueDate", "shortName", "companyName",
                        "area", "provinceCode", "proviceName", "cityCode", "cityName",
                        "districtCode", "districtName", "minAreaCode", "deadline", "estimatedYield", "frLevel",
                        "ccxiIndustryPermissionId", "ccxiIndustryPermissionName", "companyType", "companyTypeName",
                        "companyCode", "creditRating", "bondType", "bondTypeName", "bondRating", "biddingInterval",
                        "datumCurve", "excessSpread", "rate", "difference", "isValid", "credit_code", "secode", "ccxi_l1_code",
                        "ccxi_l1_name", "ccxi_l2_code", "ccxi_l2_name", "ccxi_l3_code", "ccxi_l3_name",
                        "sync_time"]
        self.api = ApiCaller(logger=logger, default_headers={"X-CCXI-EXTERNAL-Token": Profile.token})

    def __queryEnterprise__(self, compcode=None):
        sql = """
            SELECT enterprise_name, credit_code, comp_code,
            ccxi_l1_code, ccxi_l1_name,ccxi_l2_code,ccxi_l2_name,ccxi_l3_code, ccxi_l3_name
            FROM ent_enterprise
        """
        if compcode is None:
            return self.localDb.Query(sql)
        else:
            sql += " where comp_code = %s "
            return self.localDb.Query(sql, (compcode,))

    def __querySecode__(self, shortName=None):
        sql = """
        SELECT short_name, secode FROM deb_debenture_base 
        """
        if shortName is None:
            return self.localDb.Query(sql)
        else:
            sql += " where short_name = %s "
            return self.localDb.Query(sql, (shortName,))

    def __queryMaxSyncTime__(self):
        sql = f"""
            select max(sync_time) as maxSyncTime from bond_new_valuation
        """
        r = self.localDb.QueryOne(sql)
        if r is None or r["maxSyncTime"] is None:
            return ""
        return r["maxSyncTime"]

    def __queryBondNewValuationData__(self, maxSyncTime, page, size) -> dict:
        postBody = {
            "shortName": None,
            "companyName": None,
            "beginDate": maxSyncTime,
            "endDate": None,
            "current": page,
            "size": size
        }
        return self.api.post(url=Profile.bondNewValuationData, data=postBody)

    def __updateLocal__(self, updateDatas):
        # 生成sql
        sql = "REPLACE INTO bond_new_valuation "
        sql += "("
        sql += ",".join(self.columns)
        sql += ") VALUES ("
        sql += ",".join(list("%(" + c + ")s" for c in self.columns))
        sql += ")"
        return self.localDb.ExecMany(sql, updateDatas)

    def sync(self):

        # 解析参数
        self.isAll = CommonUtils.isCommandParam(sys.argv, "all")["exist"]

        if self.isAll:
            self.logger.warn(CommonUtils.createColorText("全量更新模式", "red"))

        maxSyncTime = ""
        if self.isAll:
            # 全量模式下加载全量缓存
            self.logger.info("加载企业名单")
            enterpriseList = self.__queryEnterprise__()
            self.cacheEnterprise = {d["comp_code"]: d for d in enterpriseList}
            self.logger.info(f"加载企业名单 {len(self.cacheEnterprise)}")

            self.logger.info("加载债券名单")
            secodes = self.__querySecode__()
            self.cacheSecode = {d["short_name"]: d["secode"] for d in secodes}
            self.logger.info(f"加载债券名单 {len(self.cacheSecode)}")

            self.size = 1000
        else:
            # 确定更新时间点
            maxSyncTime = self.__queryMaxSyncTime__()

        total = 0
        page = 1
        self.logger.info(f"最后更新时间 {'----' if self.isAll else maxSyncTime}")
        while True:
            syncTime = datetime.datetime.now()
            result = self.__queryBondNewValuationData__(maxSyncTime=maxSyncTime, page=page, size=self.size)
            if result is not None:
                dataList = result["dataList"]
                for d in dataList:
                    # 企业数据
                    enterprise = self.__getEnterprise__(d["companyCode"])
                    if enterprise is None:
                        d["credit_code"] = None
                        d["ccxi_l1_code"] = None
                        d["ccxi_l1_name"] = None
                        d["ccxi_l2_code"] = None
                        d["ccxi_l2_name"] = None
                        d["ccxi_l3_code"] = None
                        d["ccxi_l3_name"] = None
                    else:
                        d["credit_code"] = enterprise["credit_code"]
                        d["ccxi_l1_code"] = enterprise["ccxi_l1_code"]
                        d["ccxi_l1_name"] = enterprise["ccxi_l1_name"]
                        d["ccxi_l2_code"] = enterprise["ccxi_l2_code"]
                        d["ccxi_l2_name"] = enterprise["ccxi_l2_name"]
                        d["ccxi_l3_code"] = enterprise["ccxi_l3_code"]
                        d["ccxi_l3_name"] = enterprise["ccxi_l3_name"]
                    # 债券数据
                    d["secode"] = self.__getSecode__(d["shortName"])
                    # 同步时间
                    d["sync_time"] = syncTime
                self.logger.info(f"加载数据 {len(dataList)} 条")
                self.__updateLocal__(updateDatas=dataList)
                total += len(dataList)
                self.logger.info(f"更新 {len(dataList)}，合计更新 {total}")
                if not result["hasNext"]:
                    break
                page += 1
            else:
                break

    def __getEnterprise__(self, compcode: str) -> dict:
        if compcode is None:
            return None
        # 命中缓存
        if compcode in self.cacheEnterprise.keys():
            return self.cacheEnterprise.get(compcode)
        if not self.isAll:
            enterprise = self.__queryEnterprise__(compcode)
            if len(enterprise) > 0:
                self.cacheEnterprise[compcode] = enterprise[0]
                return enterprise[0]
            else:
                self.cacheEnterprise[compcode] = None
                return None
        return None

    def __getSecode__(self, shortName: str) -> str:
        if shortName is None:
            return None
        if shortName in self.cacheSecode.keys():
            return self.cacheSecode.get(shortName)
        if not self.isAll:
            secodes = self.__querySecode__(shortName)
            if len(secodes) > 0:
                self.cacheSecode[shortName] = secodes[0]["secode"]
                return secodes[0]["secode"]
            else:
                self.cacheSecode[shortName] = None
                return None
        return None


def main():
    logger = Logger()
    CommonUtils.cleanScreen()
    logger.info("============新发债估值数据 开始================")

    localDb = LocalDb(logSql=False, autocommit=True)
    dcDb = CcxDataCenterProd(logSql=False, autocommit=True)

    sync = SyncBondNewValuation(logger=logger, localDb=localDb, dcDb=dcDb)
    sync.sync()
    logger.info("============新发债估值数据 结束================")


# 程序入口
if __name__ == "__main__":
    main()
